using System.Collections;
using Microsoft.Xna.Framework.Graphics;
using Tomahawk.Runtime.Scene;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;

namespace Tomahawk.Runtime.Rendering
{
    public class PostProcessor
    {
        private string shaderAsset;

        protected Effect shaderEffect;
        protected SpriteBatch spriteBatch;
        protected RenderTarget2D renderTarget;

        protected PostProcessor nextProcessor;

        public delegate void PostProcessorEffect(Effect effect);
        public event PostProcessorEffect OnEffectBegin;

        /// <summary>
        /// Constructor
        /// </summary>
        public PostProcessor(string shaderAsset)
        {
            this.shaderAsset = shaderAsset;
        }

        /// <summary>
        /// The shader effect
        /// </summary>
        public Effect Effect
        {
            get { return shaderEffect; }
        }

        /// <summary>
        /// Load the assets
        /// </summary>
        public virtual void LoadGraphicsContent(GraphicsDevice device, ContentManager loader)
        {
            //create the shader
            shaderEffect = loader.Load<Effect>(shaderAsset);

            //create the render target
            renderTarget = new RenderTarget2D(
                    device,
                    device.Viewport.Width,
                    device.Viewport.Height,
                    1,
                    device.DisplayMode.Format
                );

            //create the sprite batch
            spriteBatch = new SpriteBatch(device);
        }

        /// <summary>
        /// Activate the render target of this processor
        /// </summary>
        internal void activateRenderTarget(GraphicsDevice device)
        {
            //activate this processor's rendertarget
            device.SetRenderTarget(0, renderTarget);
        }

        /// <summary>
        /// Deactivates the render targe of this processor
        /// </summary>
        protected void resolveRenderTarget(GraphicsDevice device)
        {
            //deactivate this processor's rendertarget
            //device.ResolveRenderTarget(0);
        }

        /// <summary>
        /// Pre-Render Stage. 
        /// (Only the last processor on the chain will activate its render-target)
        /// </summary>
        public void PreRender(GraphicsDevice device)
        {
            //do the next processor pre-render stage
            if (this.nextProcessor != null)
                this.nextProcessor.PreRender(device);
            else
            {
                activateRenderTarget(device);
            }
        }

        /// <summary>
        /// Post-Render Stage
        /// </summary>
        public void PostRender(GraphicsDevice device, PostProcessor previousProcessor)
        {
            if (this.nextProcessor != null)
            {
                //We are not the last processor, so
                this.nextProcessor.PostRender(device, this);      //wait for the next processor to end
                drawOnPreviousProcessor(device, previousProcessor);
            }
            else
            {
                //We are the last procesor of the chain, so
                drawOnPreviousProcessor(device, previousProcessor);
            }
        }

        /// <summary>
        /// Draw this processor result's on previous processor. 
        /// If it's there no previous then render in main screen
        /// </summary>
        protected virtual void drawOnPreviousProcessor(GraphicsDevice device, PostProcessor previousProcessor)
        {
            this.resolveRenderTarget(device);   //the scene is drawn, resolve target
            if (previousProcessor != null)      //if it's there a previous processor
                previousProcessor.activateRenderTarget(device);  //activate it
            else
                device.SetRenderTarget(0, null);//if it's not, activate main screen
            this.renderQuad(device, shaderEffect, renderTarget.GetTexture()); //draw the quad in the active processor
        }

        protected virtual void renderQuad(GraphicsDevice device, Effect shader, Texture2D texture)
        {
            renderQuad(device, shader, texture, new Rectangle(0, 0, texture.Width, texture.Height));
        }
        /// <summary>
        /// Render the sprite using the shader effect
        /// </summary>
        protected virtual void renderQuad(GraphicsDevice device, Effect shader, Texture2D texture, Rectangle destRect)
        {
            //let the client code personalize shader effects
            if (OnEffectBegin != null) OnEffectBegin(shader);

            shader.Begin();
            spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.SaveState);

            foreach (EffectPass pass in shader.CurrentTechnique.Passes)
            {
                pass.Begin();
                spriteBatch.Draw(texture, destRect, null, Color.White);
                pass.End();
            }

            spriteBatch.End();
            shader.End();
        }

        /// <summary>
        /// Sets the next processor on a chain
        /// </summary>
        internal void setNextProcessor(PostProcessor nextProcessor)
        {
            this.nextProcessor = nextProcessor;
        }

        /// <summary>
        /// Destroy the post-processor
        /// </summary>
        public virtual void Destroy()
        {
            if (this.nextProcessor != null)
                this.nextProcessor.Destroy();

            this.shaderEffect = null;
            this.spriteBatch = null;
            this.renderTarget = null;
        }
    }

}

